<!DOCTYPE html>
<html>

<!--
Copyright 2022 The IREE Authors

Licensed under the Apache License v2.0 with LLVM Exceptions.
See https://llvm.org/LICENSE.txt for license information.
SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-->

<head>
  <meta charset="utf-8" />
  <title>IREE Static Web Sample</title>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="icon" href="./IREE_Logo_Icon_Color.svg" type="image/svg+xml">

  <script src="./easeljs.min.js"></script>
  <script src="./iree_api.js"></script>
</head>

<body style="background-color: #2b2c30; color: #ABB2BF">
  <h1>IREE Static Web Sample</h1>

  <canvas id="drawingCanvas" width="256" height="256"
          style="border:2px solid #000000; background-color: #FFFFFF;"
          oncontextmenu="return false;">
  </canvas>
  <canvas id="rescaledCanvas" width="28" height="28"
          style="border:2px solid #000000; background-color: #FFFFFF;">
  </canvas>

  <br>
  <div style="border:2px solid #000000; background-color: #CCCCCC; padding: 8px; color: #111111; width:440px">
    <button id="predictButton" disabled onclick="predictDigit()">Predict handwritten digit</button>
    <button id="clearCanvasButton" onclick="clearCanvas()">Clear canvas</button>
    <br>
    Prediction result: <div id="predictionResult" style="display:inline"></div>
  </div>

  <script>
    // <canvas> drawing using easeljs forked from:
    //   https://createjs.com/demos/easeljs/curveto
    //   https://github.com/CreateJS/EaselJS/blob/master/examples/CurveTo.html

    const predictButtonElement = document.getElementById('predictButton');
    const predictionResultElement = document.getElementById('predictionResult');
    const drawingCanvasElement = document.getElementById("drawingCanvas");
    const rescaledCanvasElement = document.getElementById("rescaledCanvas");
    const rescaledCanvasContext = rescaledCanvasElement.getContext("2d");
    let stage;
    let drawingCanvasShape;
    let oldPt, oldMidPt;
    let titleText;
    let ireeInitialized = false;
    const primaryColor = "#000000";
    const eraseColor = "#FFFFFF";
    const stroke = 32;

    function predictDigit() {
      // TODO(scotttodd): debounce / rate limit this?
      ireePredictDigit(getRescaledCanvasData()).then((result) => {
        predictionResultElement.innerHTML = result;
      }).catch((error) => {
        console.error('error predicting digit:', error);
        predictionResultElement.innerHTML = "<b>" + error + "</b>";
      });
    }

    function clearCanvas() {
      stage.clear();
      stage.removeAllChildren();

      drawingCanvasShape = new createjs.Shape();
      stage.addChild(drawingCanvasShape);
      stage.update();

      updateRescaledCanvas();
    }

    function initDrawing() {
      rescaledCanvasContext.imageSmoothingEnabled = false;
      rescaledCanvasContext.mozImageSmoothingEnabled = false;
      rescaledCanvasContext.webkitImageSmoothingEnabled = false;
      rescaledCanvasContext.msImageSmoothingEnabled = false;

      stage = new createjs.Stage(drawingCanvasElement);
      stage.autoClear = false;
      stage.enableDOMEvents(true);

      createjs.Touch.enable(stage);
      createjs.Ticker.framerate = 24;

      stage.addEventListener("stagemousedown", handleMouseDown);
      stage.addEventListener("stagemouseup", handleMouseUp);

      drawingCanvasShape = new createjs.Shape();
      stage.addChild(drawingCanvasShape);

      // Add instruction text.
      titleText = new createjs.Text("Click and Drag to draw", "18px Arial", "#000000");
      titleText.x = 30;
      titleText.y = 100;
      stage.addChild(titleText);

      stage.update();
    }

    function handleMouseDown(event) {
      if (!event.primary && !event.secondary) { return; }

      if (stage.contains(titleText)) {
        stage.clear();
        stage.removeChild(titleText);
      }

      oldPt = new createjs.Point(stage.mouseX, stage.mouseY);
      oldMidPt = oldPt.clone();
      stage.addEventListener("stagemousemove", handleMouseMove);
    }

    function handleMouseMove(event) {
      if (!event.primary && !event.secondary) { return; }

      const midPt = new createjs.Point(
        oldPt.x + stage.mouseX >> 1, oldPt.y + stage.mouseY >> 1);

      const color = event.nativeEvent.which == 1 ? primaryColor : eraseColor;
      drawingCanvasShape.graphics.clear()
          .setStrokeStyle(stroke, 'round', 'round')
          .beginStroke(color).moveTo(midPt.x, midPt.y)
          .curveTo(oldPt.x, oldPt.y, oldMidPt.x, oldMidPt.y);

      oldPt.x = stage.mouseX;
      oldPt.y = stage.mouseY;
      oldMidPt.x = midPt.x;
      oldMidPt.y = midPt.y;

      stage.update();
      updateRescaledCanvas();

      if (ireeInitialized) {
        predictDigit();
      }
    }

    function handleMouseUp(event) {
      if (!event.primary && !event.default) { return; }
      stage.removeEventListener("stagemousemove", handleMouseMove);
    }

    function updateRescaledCanvas() {
      rescaledCanvasContext.clearRect(
        0, 0, rescaledCanvasElement.width, rescaledCanvasElement.height);
      rescaledCanvasContext.drawImage(
          drawingCanvasElement,
          /*sx=*/0, /*sy=*/0,
          /*sWidth=*/256, /*sHeight=*/256,
          /*dx=*/0, /*dy=*/0,
          /*dWidth=*/28, /*dHeight=*/28);
    }

    function getRescaledCanvasData() {
      return rescaledCanvasContext.getImageData(0, 0, 28, 28);
    }

    initDrawing();

    ireeInitializeWorker().then((result) => {
      predictButtonElement.disabled = false;
      ireeInitialized = true;
    }).catch((error) => {
      console.error("Failed to initialize IREE, error: '" + error + "'");
    });
  </script>
</body>

</html>
